package com.small.handler;

import java.io.File;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.log4j.Logger;

import com.small.annotation.Aspect;
import com.small.annotation.Controller;
import com.small.annotation.GlobalAspect;
import com.small.context.Context;
import com.small.utils.BaseUtil;

import net.sf.json.JSONObject;

/**
 * Class助手类
 * @author 乐乐
 */
public class ClassHelper {
  
  private static final Logger logger = Logger.getLogger(ClassHelper.class);
  private static final Object object = new Object();
  private static String platform = null;

  private static Set<String> packageList = new HashSet<String>();    //所有classes下的包
  
  /**
   * 所有使用了@Controller的拦截器类（拦截器既是Aop功能中的切面）
   */
  private static List<Class<?>> controllerListClass = new ArrayList<Class<?>>();
  

  /**
   * 所有使用了@Aspect的拦截器类（拦截器既是Aop功能中的切面）
   */
  private static List<Class<?>> AspectListClass = new ArrayList<Class<?>>();
  
  /**
   * 所有使用了@GlobalAspect的默认全局切面类
   */
  private static List<Class<?>> GlobalAspectListClass = new ArrayList<Class<?>>();
  

  /**获取系统文件分隔符
   */
  private static String fs = System.getProperties().getProperty("file.separator");  

  /**
   * 项目下所有class类
   */
  public static Set<Class<?>> classes = new HashSet<Class<?>>();
  
  /**
   * 获取classes下所有的文件
   */
  private static String path = ClassHelper.class.getClassLoader().getResource("/").getPath().substring(1);

  public static void init() {
    platform = BaseUtil.pathSeparator(path);
    String packageName = "";
    File root = new File(path);
    try {
        loop(root, packageName);
    }catch (Exception e){
        logger.error(e.toString());
    }
    
  }

  /**获取项目下所有类
   * @return
   */
  public static Set<Class<?>> getClassAll(){
      return classes;
  }
  
  /**
   * 获取类加载器
   * @return
   */
  public static ClassLoader getClassLoader() {
    return Thread.currentThread().getContextClassLoader();
  }
  
  /**获取所有使用了@GlobalAspect的默认全局拦截器（拦截器既是aop）
   * @return
   */
  public static List<Class<?>> getGlobalAspectListClass() {
    return GlobalAspectListClass;
  }
  
  /**获取所有使用了@Aspect的拦截器类（拦截器既是aop）
   * @return
   */
  public static List<Class<?>> getAspectListClass() {
    return AspectListClass;
  }
  
  /**
   * 获取所有使用了@Controller注解的类
   * @return
   */
  public static List<Class<?>> getControllerListClass() {
    return controllerListClass;
  }
  
  /**
   * 获取所有包，并通过包获取所有类
   * @param folder
   * @param packageName
   * @throws Exception
   */
  private static void loop(File folder, String packageName) throws Exception {
      File[] files = folder.listFiles();
      for (int fileIndex = 0; fileIndex < files.length; fileIndex++) {
          File file = files[fileIndex];
          if (!BaseUtil.useLoop(file.toString())){
              if (file.isDirectory()) {
                  loop(file, packageName + file.getName() + ".");
              }
              else {
                  String filePath = file.toString();
                  //只缓存class文件
                  if (filePath.endsWith(".class")) {
                    int index = 0;
                    index = platform.length();
                    int index2 = filePath.lastIndexOf(fs);
                    int index3 = filePath.lastIndexOf(".");
                    if (0 == index){
                        break;
                    }
                    String path = filePath.substring(index,index2);
                    String packAge = path.replace(fs,".");
                    packageList.add(packAge);
                    String aClass = filePath.substring(index2 + 1, index3);
                    listMethodNames(aClass, packAge);
                  }
              }
          }
      }
  }
  
  
  /**
   * 获取包下所有类
   * @param filename
   * @param packAge
   */
  private static void listMethodNames(String filename, String packAge) {
      try {
        String url = packAge.replace(fs, ".");
        String cla = url + "." + filename;
        Class<?> aClass = Class.forName(cla);
        classes.add(aClass);
        
        if (aClass.isAnnotationPresent(Controller.class)) {
          //获取使用了@Controller注解的action类
          controllerListClass.add(aClass);
        }
        if (aClass.isAnnotationPresent(Aspect.class)) {
          //获取使用了@Aspect注解的切面类
          AspectListClass.add(aClass);
        }
        
        if (aClass.isAnnotationPresent(GlobalAspect.class)) {
          //获取使用了@GlobalAspect注解的默认全局切面类
          GlobalAspectListClass.add(aClass);
        }
        
      } catch (Exception e) {
          logger.error(e.toString());
      }
  }

  /**
   * 根据请求的Url获取里面的注解值与路由
   * @param url
   * @param platform
   * @return
   */
  public static Map<String, String> annotation(String url, String platform) {
      int index = platform.length();
      int index2 = url.lastIndexOf(fs);
      String annotation = url.substring(index, index2); //注解路径值
      String method = url.substring(index2 + 1); //服务方法名
      Map<String, String> map = new HashMap<String, String>();
      map.put("annotation", annotation);
      map.put("method", method);
      return map;
  }

  /**
   * 判断请求是否携带文件，并设置请求格式
   * @param request
   * @return 请求格式
   */
  public static String getRequestType(HttpServletRequest request, Context context){
      if (!ServletFileUpload.isMultipartContent(request)) {  //普通请求
          context.setRequestType("Json");
      }else if (ServletFileUpload.isMultipartContent(request)){ //文件请求
          context.setRequestType("File");
      }
      return context.getRequestType();
  }

  /**
   * 获取客户端请求参数中所有的信息 普通Json请求
   * @param request
   * @return
   */
  public static JSONObject getAllRequestParam(HttpServletRequest request, Context context){
      context.setRequestType("Json");
      Map<String, String> res = new HashMap<String, String>();
      Enumeration<?> temp = request.getParameterNames();
      if (null != temp) {
          while (temp.hasMoreElements()) {
              String en = (String) temp.nextElement();
              String value = request.getParameter(en);
              res.put(en, value);
              //如果字段的值为空，判断若值为空，则删除这个字段
              if (null == res.get(en) || "".equals(res.get(en))) {
                  res.remove(en);
              }
          }
      }
      JSONObject data = JSONObject.fromObject(res);
      return data;
  }

  /**
   * 当请求中包含文件时，获取流并把流存入上下文中的List<InputStream> 中 ，并且获取请求中的普通数据，把数据加入parameter中
   * @param request
   * @return List<InputStream>
   * @throws Exception
   */
  public static List<InputStream> getInputStream(HttpServletRequest request, Context context) throws Exception{
      synchronized (object) {
          List<InputStream> inputStreams = new ArrayList<InputStream>();
          //设置该线程下的项目资源文件classes为临时根目录
          String path = Thread.currentThread().getContextClassLoader().getResource("").getPath();
          String temporary = path + fs + "temporary";
          //创建文件工厂，用于处理文件
          DiskFileItemFactory factory = new DiskFileItemFactory();
          File f = new File(temporary); //判断临时目录是否存在，不存在创建
          if (!f.exists() && !f.isDirectory()){
              f.mkdir();
          }
          //设置文件上传的临时目录
          factory.setRepository(f);
          //文件核心类
          ServletFileUpload upload = new ServletFileUpload(factory);
          //使用ServletFileUpload解析上传数据，析结果返回的是一个List<FileItem>集合，每一个FileItem对应一个Form表单的输入项
          List<FileItem> items =upload.parseRequest(request);
          Map<String, String> parameter = new HashMap<String, String>();
          for (FileItem item : items) {
              if (item.isFormField()) { //普通数据
                  String filedName = item.getFieldName();//普通输入项数据的名
                  //解决普通输入项的数据的中文乱码问题
                  String filedValue = item.getString("UTF-8");//普通输入项的值
                  parameter.put(filedName, filedValue);   //把普通数据加入parameter中
              }else {    //文件数据
                  String fileName = item.getName();//上传文件的名
                  //多个文件上传输入框有空 的 异常处理
                  if(fileName==null||"".equals(fileName.trim())){  //去空格是否为空
                      continue;// 为空，跳过当次循环，  第一个没输入则跳过可以继续输入第二个
                  }
                  //注意：不同的浏览器提交的文件名是不一样的，有些浏览器提交上来的文件名是带有路径的，如：  c:\a\b\1.txt，而有些只是单纯的文件名，如：1.txt
                  fileName = fileName.substring(fileName.lastIndexOf(fs)+1);//截取文件名
                  InputStream in = item.getInputStream(); //获取item中的上传文件的输入流
                  inputStreams.add(in);
                  //删除临时文件
                  item.delete();//删除处理文件上传时生成的临时文件
              }
          }
          if (null != parameter)
              context.setParameter(JSONObject.fromObject(parameter));

          return inputStreams;
      }
  }
  
  
  /**
   * 执行请求 
   * @param tagerClass  目标类
   * @param method  目标方法
   * @param object  目标类
   * @param context 请求上下文
   */
  public static void invokeMethod2(Class<?> tagerClass, Method method, Object... context) {
    
    Object bean = BeanHelper.getBean(tagerClass);
    try {
      //设置请求路径时需注意  默认控制层不应存在除public以外的接口  对内接口不要写在对外控制层
      //值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查 关闭安全检查 提高大约20倍性能
      method.setAccessible(true);
      //目标类和目标方法不允许为空
      if (ObjectUtils.isEmpty(bean)) {
        BeanHelper.setBean(tagerClass, tagerClass.newInstance());
      }
      //默认返回null  Context上下文会在请求结束时输出请求结果
      method.invoke(BeanHelper.getBean(tagerClass), context);
      
    } catch (IllegalAccessException e) {
      logger.error(e.toString());
    } catch (IllegalArgumentException e) {
      logger.error(e.toString());
    } catch (InvocationTargetException e) {
      logger.error(e.toString());
    } catch (InstantiationException e) {
      logger.error(e.toString());
    }
  }

}
